home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 201-225 / disk_217 / stevie / search.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  22KB  |  1,048 lines

  1. /*
  2.  * STEVIE - Simply Try this Editor for VI Enthusiasts
  3.  *
  4.  * Code Contributions By : Tim Thompson           twitch!tjt
  5.  *                         Tony Andrews           onecom!wldrdg!tony 
  6.  *                         G. R. (Fred) Walter    watmath!watcgl!grwalter 
  7.  */
  8.  
  9. #include "stevie.h"
  10. /* modified Henry Spencer's regular expression routines */
  11. #include "regexp.h"
  12.  
  13. #ifdef  MEGAMAX
  14. overlay "search"
  15. #endif
  16.  
  17. /*
  18.  * This file contains various searching-related routines. These fall into
  19.  * three groups: string searches (for /, ?, n, and N), character searches
  20.  * within a single line (for f, F, t, T, etc), and "other" kinds of searches
  21.  * like the '%' command, and 'word' searches. 
  22.  */
  23.  
  24. /*
  25.  * String searches 
  26.  *
  27.  * The actual searches are done using Henry Spencer's regular expression
  28.  * library. 
  29.  */
  30.  
  31. #define BEGWORD "([^a-zA-Z0-9_]|^)"    /* replaces "\<" in search strings */
  32. #define ENDWORD "([^a-zA-Z0-9_]|$)"    /* likewise replaces "\>" */
  33.  
  34. bool_t begword;            /* does the search include a 'begin word'
  35.                  * match */
  36.  
  37. /*
  38.  * mapstring(s) - map special backslash sequences 
  39.  */
  40. static char    *
  41. mapstring(s)
  42.     register char  *s;
  43. {
  44.     static char     ns[MAX_COLUMNS + 1];
  45.     register char  *p;
  46.  
  47.     begword = FALSE;
  48.  
  49.     for (p = ns; *s; s++) {
  50.     if ((*s == '(') || (*s == ')')) {
  51.         *p++ = '\\';
  52.         *p++ = *s;
  53.         continue;
  54.     }
  55.     if (*s != '\\') {    /* not an escape */
  56.         *p++ = *s;
  57.         continue;
  58.     }
  59.     switch (*++s) {
  60.       case '/':
  61.         *p++ = '/';
  62.         break;
  63.  
  64.       case '<':
  65.         strcpy(p, BEGWORD);
  66.         p += strlen(BEGWORD);
  67.         begword = TRUE;
  68.         break;
  69.  
  70.       case '>':
  71.         strcpy(p, ENDWORD);
  72.         p += strlen(ENDWORD);
  73.         break;
  74.  
  75.       default:
  76.         *p++ = '\\';
  77.         *p++ = *s;
  78.         break;
  79.     }
  80.     }
  81.     *p = NUL;
  82.  
  83.     return ns;
  84. }
  85.  
  86. static LPtr    *
  87. bcksearch(str)
  88.     char           *str;
  89. {
  90.     static LPtr     infile;
  91.     register LPtr  *p;
  92.     regexp         *prog;
  93.     register char  *s;
  94.     register int    i;
  95.     bool_t          want_start = (*str == '^');    /* looking for start of line? */
  96.     register char  *match;
  97.  
  98.     /* make sure str isn't empty */
  99.     if (str == NULL || *str == NUL)
  100.     return NULL;
  101.  
  102.     prog = regcomp(str);
  103.     if (prog == NULL) {
  104.     emsg("Invalid search string");
  105.     return NULL;
  106.     }
  107.     p = Curschar;
  108.     dec(p);
  109.  
  110.     if (begword)        /* so we don't get stuck on one match */
  111.     dec(p);
  112.  
  113.     i = (want_start) ? 0 : p->index;
  114.  
  115.     do {
  116.     s = p->linep->s;
  117.  
  118.     if (regexec(prog, s, TRUE)) {    /* match somewhere on line */
  119.  
  120.         if (want_start) {    /* could only have been one */
  121.         infile.linep = p->linep;
  122.         infile.index = (int) (prog->startp[0] - s);
  123.         free((char *) prog);
  124.         return (&infile);
  125.         }
  126.         /*
  127.          * Now, if there are multiple matches on this line, we have to
  128.          * get the last one. Or the last one before the cursor, if we're
  129.          * on that line. 
  130.          */
  131.  
  132.         match = prog->startp[0];
  133.  
  134.         while (regexec(prog, prog->endp[0], FALSE)) {
  135.         if ((i >= 0) && ((prog->startp[0] - s) > i))
  136.             break;
  137.         match = prog->startp[0];
  138.         }
  139.  
  140.         if ((i >= 0) && ((match - s) > i)) {
  141.         i = -1;
  142.         continue;
  143.         }
  144.         infile.linep = p->linep;
  145.         infile.index = (int) (match - s);
  146.         free((char *) prog);
  147.         return (&infile);
  148.     }
  149.     i = -1;
  150.  
  151.     } while ((p = prevline(p)) != NULL);
  152.  
  153.     /*
  154.      * If wrapscan isn't set, bag the search now 
  155.      */
  156.     if (!P(P_WS)) {
  157.     free((char *) prog);
  158.     return NULL;
  159.     }
  160.     /* search backward from the end of the file */
  161.     p = prevline(Fileend);
  162.     do {
  163.     s = p->linep->s;
  164.  
  165.     if (regexec(prog, s, TRUE)) {    /* match somewhere on line */
  166.  
  167.         if (want_start) {    /* could only have been one */
  168.         infile.linep = p->linep;
  169.         infile.index = (int) (prog->startp[0] - s);
  170.         free((char *) prog);
  171.         return (&infile);
  172.         }
  173.         /*
  174.          * Now, if there are multiple matches on this line, we have to
  175.          * get the last one. 
  176.          */
  177.  
  178.         match = prog->startp[0];
  179.  
  180.         while (regexec(prog, prog->endp[0], FALSE))
  181.         match = prog->startp[0];
  182.  
  183.         infile.linep = p->linep;
  184.         infile.index = (int) (match - s);
  185.         free((char *) prog);
  186.         return (&infile);
  187.     }
  188.     if (p->linep == Curschar->linep)
  189.         break;
  190.  
  191.     } while ((p = prevline(p)) != NULL);
  192.  
  193.     free((char *) prog);
  194.     return NULL;
  195. }
  196.  
  197. static LPtr    *
  198. fwdsearch(str)
  199.     char           *str;
  200. {
  201.     static LPtr     infile;
  202.     LPtr           *p;
  203.     regexp         *prog;
  204.     bool_t          want_start = (*str == '^');    /* looking for start of line? */
  205.  
  206.     char           *s;
  207.     int             i;
  208.  
  209.     prog = regcomp(str);
  210.     if (prog == NULL) {
  211.     emsg("Invalid search string");
  212.     return NULL;
  213.     }
  214.     p = Curschar;
  215.     i = Curschar->index + 1;
  216.     do {
  217.     s = p->linep->s + i;
  218.     i = 0;
  219.  
  220.     if (regexec(prog, s, i == 0)) {    /* got a match */
  221.         /*
  222.          * If we wanted the start of a line and we aren't really there,
  223.          * then a match doesn't count. 
  224.          */
  225.         if (want_start && (s != p->linep->s))
  226.         continue;
  227.  
  228.         infile.linep = p->linep;
  229.         infile.index = (int) (prog->startp[0] - p->linep->s);
  230.         free((char *) prog);
  231.         return (&infile);
  232.     }
  233.     } while ((p = nextline(p)) != NULL);
  234.  
  235.     /*
  236.      * If wrapscan isn't set, then don't scan from the beginning of the file.
  237.      * Just return failure here. 
  238.      */
  239.     if (!P(P_WS)) {
  240.     free((char *) prog);
  241.     return NULL;
  242.     }
  243.     /* search from the beginning of the file to Curschar */
  244.     for (p = Filemem; p != NULL; p = nextline(p)) {
  245.     s = p->linep->s;
  246.  
  247.     if (regexec(prog, s, TRUE)) {    /* got a match */
  248.         infile.linep = p->linep;
  249.         infile.index = (int) (prog->startp[0] - s);
  250.         free((char *) prog);
  251.         return (&infile);
  252.     }
  253.     if (p->linep == Curschar->linep)
  254.         break;
  255.     }
  256.  
  257.     free((char *) prog);
  258.     return (NULL);
  259. }
  260.  
  261. static char    *laststr = NULL;
  262. static int      lastsdir;
  263.  
  264. static LPtr    *
  265. ssearch(dir, str)
  266.     int             dir;    /* FORWARD or BACKWARD */
  267.     char           *str;
  268. {
  269.     LPtr           *pos;
  270.  
  271.     reg_ic = P(P_IC);        /* tell the regexp routines how to search */
  272.  
  273.     if (laststr != str) {
  274.     if (laststr != NULL)
  275.         free(laststr);
  276.     laststr = strsave(str);
  277.     }
  278.     lastsdir = dir;
  279.  
  280.     if (dir == BACKWARD)
  281.     pos = bcksearch(mapstring(str));
  282.     else
  283.     pos = fwdsearch(mapstring(str));
  284.  
  285.     /*
  286.      * This is kind of a kludge, but its needed to make 'beginning of word'
  287.      * searches land on the right place. 
  288.      */
  289.     if (pos != NULL && begword) {
  290.     if (pos->index != 0)
  291.         pos->index += 1;
  292.     }
  293.     return pos;
  294. }
  295.  
  296. bool_t
  297. dosearch(dir, str)
  298.     int             dir;
  299.     char           *str;
  300. {
  301.     LPtr           *p;
  302.  
  303.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  304.  
  305.     if ((p = ssearch(dir, str)) == NULL) {
  306.     msg("Pattern not found");
  307.     return (FALSE);
  308.     } else {
  309.     LPtr            savep;
  310.  
  311.     /* if we're backing up, we make sure the line we're on */
  312.     /* is on the screen. */
  313.     setpcmark();
  314.     *Curschar = savep = *p;
  315.  
  316.     return (TRUE);
  317.     }
  318. }
  319.  
  320. void
  321. searchagain(dir)
  322.     int             dir;
  323. {
  324.     if (laststr == NULL)
  325.     beep();
  326.     else
  327.     dosearch(dir, laststr);
  328.  
  329.     lastsdir = dir;
  330. }
  331.  
  332. #define OTHERDIR(x)     (((x) == FORWARD) ? BACKWARD : FORWARD)
  333.  
  334. bool_t
  335. repsearch(flag)
  336.     bool_t          flag;
  337. {
  338.     int             dir = lastsdir;
  339.     bool_t          found;
  340.  
  341.     if (laststr == NULL) {
  342.     beep();
  343.     return FALSE;
  344.     }
  345.     found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
  346.  
  347.     /*
  348.      * We have to save and restore 'lastsdir' because it gets munged by
  349.      * ssearch() and winds up saving the wrong direction from here if 'flag'
  350.      * is true. 
  351.      */
  352.     lastsdir = dir;
  353.  
  354.     return (found);
  355. }
  356.  
  357. /*
  358.  * regerror - called by regexp routines when errors are detected. 
  359.  */
  360. void
  361. regerror(s)
  362.     char           *s;
  363. {
  364.     emsg(s);
  365. }
  366.  
  367. /*
  368.  * dosub(lp, up, cmd)
  369.  *
  370.  * Perform a substitution from line 'lp' to line 'up' using the
  371.  * command pointed to by 'cmd' which should be of the form:
  372.  *
  373.  * /pattern/substitution/g
  374.  *
  375.  * The trailing 'g' is optional and, if present, indicates that multiple
  376.  * substitutions should be performed on each line, if applicable.
  377.  * The usual escapes are supported as described in the regexp docs.
  378.  */
  379.  
  380. void
  381. dosub(lp, up, cmd)
  382.     LPtr           *lp, *up;
  383.     char           *cmd;
  384. {
  385.     LINE           *cp;
  386.     char           *pat, *sub;
  387.     regexp         *prog;
  388.     int             nsubs;
  389.     bool_t          do_all;    /* do multiple substitutions per line */
  390.     int             n;
  391.  
  392.     /*
  393.      * If no range was given, do the current line. If only one line was
  394.      * given, just do that one. 
  395.      */
  396.     if (lp->linep == NULL)
  397.     *up = *lp = *Curschar;
  398.     else {
  399.     if (up->linep == NULL)
  400.         *up = *lp;
  401.     }
  402.  
  403.     pat = ++cmd;        /* skip the initial '/' */
  404.  
  405.     while (*cmd) {
  406.     if (cmd[0] == '/' && cmd[-1] != '\\') {
  407.         *cmd++ = NUL;
  408.         break;
  409.     }
  410.     cmd++;
  411.     }
  412.  
  413.     if (*pat == NUL) {
  414.     emsg("NULL pattern specified");
  415.     return;
  416.     }
  417.     sub = cmd;
  418.  
  419.     do_all = FALSE;
  420.  
  421.     while (*cmd) {
  422.     if (cmd[0] == '/' && cmd[-1] != '\\') {
  423.         do_all = (cmd[1] == 'g');
  424.         *cmd = NUL;
  425.         break;
  426.     }
  427.     cmd++;
  428.     }
  429.  
  430.     reg_ic = P(P_IC);        /* set "ignore case" flag appropriately */
  431.  
  432.     prog = regcomp(pat);
  433.     if (prog == NULL) {
  434.     emsg("Invalid search string");
  435.     return;
  436.     }
  437.     nsubs = 0;
  438.  
  439.     ResetBuffers();
  440.     n = RowNumber(lp);
  441.  
  442.     cp = lp->linep;
  443.     for (; cp != Fileend->linep && cp != NULL; cp = cp->next, n++) {
  444.     if (regexec(prog, cp->s, TRUE)) {    /* a match on this line */
  445.         char           *ns, *sns, *p;
  446.  
  447.         /*
  448.          * Save the line that was last changed for the final cursor
  449.          * position (just like the real vi). 
  450.          */
  451.         Curschar->linep = cp;
  452.  
  453.         /*
  454.          * Get some space for a temporary buffer to do the substitution
  455.          * into. 
  456.          */
  457.         sns = ns = alloc(2048);
  458.         if (ns == NULL)
  459.         break;
  460.  
  461.         *sns = NUL;
  462.  
  463.         p = cp->s;
  464.  
  465.         do {
  466.         for (ns = sns; *ns; ns++);
  467.         /*
  468.          * copy up to the part that matched 
  469.          */
  470.         while (p < prog->startp[0])
  471.             *ns++ = *p++;
  472.  
  473.         regsub(prog, sub, ns);
  474.  
  475.         /*
  476.          * continue searching after the match 
  477.          */
  478.         p = prog->endp[0];
  479.  
  480.         } while (regexec(prog, p, FALSE) && do_all);
  481.  
  482.         for (ns = sns; *ns; ns++);
  483.  
  484.         /*
  485.          * copy the rest of the line, that didn't match 
  486.          */
  487.         while (*p)
  488.         *ns++ = *p++;
  489.  
  490.         *ns = NUL;
  491.  
  492.         AppendPositionToUndoUndobuff(0, n);
  493.         AppendPositionToUndobuff(0, n);
  494.         AppendToUndoUndobuff("c$");
  495.         AppendToUndobuff("c$");
  496.         AppendToUndoUndobuff(sns);
  497.         AppendToUndobuff(cp->s);
  498.         AppendToUndoUndobuff(ESC_STR);
  499.         AppendToUndobuff(ESC_STR);
  500.  
  501.         free(cp->s);    /* free the original line */
  502.         cp->s = strsave(sns);    /* and save the modified str */
  503.         cp->size = strlen(cp->s) + 1;
  504.         free(sns);        /* free the temp buffer */
  505.         nsubs++;
  506.     }
  507.     if (cp == up->linep)
  508.         break;
  509.     }
  510.  
  511.     if (nsubs) {
  512.     CHANGED;
  513.     S_NOT_VALID;
  514.     AppendPositionToUndoUndobuff(0, 1);
  515.     AppendPositionToUndobuff(0, 1);
  516.     beginline(TRUE);
  517.     if (nsubs >= P(P_RP))
  518.         smsg("%d substitution%c", nsubs, (nsubs > 1) ? 's' : ' ');
  519.     } else
  520.     msg("No match");
  521.  
  522.     free((char *) prog);
  523. }
  524.  
  525. /*
  526.  * doglob(cmd)
  527.  *
  528.  * Execute a global command of the form:
  529.  *
  530.  * g/pattern/X
  531.  *
  532.  * where 'x' is a command character, currently one of the following:
  533.  *
  534.  * d    Delete all matching lines
  535.  * p    Print all matching lines
  536.  *
  537.  * The command character (as well as the trailing slash) is optional, and
  538.  * is assumed to be 'p' if missing.
  539.  */
  540.  
  541. void
  542. doglob(lp, up, cmd)
  543.     LPtr           *lp, *up;
  544.     char           *cmd;
  545. {
  546.     LINE           *cp;
  547.  
  548.     char           *pat;
  549.     regexp         *prog;
  550.     int             ndone;
  551.     char            cmdchar = NUL;    /* what to do with matching lines */
  552.     int             nu;
  553.     int             nuu = 0;
  554.  
  555.     /*
  556.      * If no range was given, do every line. If only one line was given, just
  557.      * do that one. 
  558.      */
  559.     if (lp->linep == NULL) {
  560.     *lp = *Filemem;
  561.     *up = *Fileend;
  562.     } else {
  563.     if (up->linep == NULL)
  564.         *up = *lp;
  565.     }
  566.  
  567.     pat = ++cmd;        /* skip the initial '/' */
  568.  
  569.     while (*cmd) {
  570.     if (cmd[0] == '/' && cmd[-1] != '\\') {
  571.         cmdchar = cmd[1];
  572.         *cmd = NUL;
  573.         break;
  574.     }
  575.     cmd++;
  576.     }
  577.     if (cmdchar == NUL)
  578.     cmdchar = 'p';
  579.  
  580.     reg_ic = P(P_IC);        /* set "ignore case" flag appropriately */
  581.  
  582.     if (cmdchar != 'd' && cmdchar != 'p') {
  583.     emsg("Invalid command character");
  584.     return;
  585.     }
  586.     prog = regcomp(pat);
  587.     if (prog == NULL) {
  588.     emsg("Invalid search string");
  589.     return;
  590.     }
  591.     msg("");
  592.     ndone = 0;
  593.  
  594.     nu = RowNumber(lp);
  595.     if (cmdchar == 'd') {
  596.     ResetBuffers();
  597.     nuu = nu;
  598.     }
  599.     cp = lp->linep;
  600.     for (; cp != Fileend->linep && cp != NULL; cp = cp->next, nu++) {
  601.     if (regexec(prog, cp->s, TRUE)) {    /* a match on this line */
  602.         Curschar->linep = cp;
  603.         Curschar->index = 0;
  604.  
  605.         switch (cmdchar) {
  606.  
  607.           case 'd':    /* delete the line */
  608.         AppendPositionToUndoUndobuff(0, nuu);
  609.         AppendToUndoUndobuff("dd");
  610.         if (buf1line() && (ndone == 0)) {
  611.             AppendToUndobuff("a");
  612.         } else if (buf1line()) {
  613.             AppendToUndobuff("j");
  614.             AppendToUndobuff("I");
  615.         } else if (cp->next == Fileend->linep) {
  616.             AppendPositionToUndobuff(0, nu);
  617.             AppendToUndobuff("o");
  618.         } else {
  619.             AppendPositionToUndobuff(0, nu);
  620.             AppendToUndobuff("O");
  621.         }
  622.         AppendToUndobuff(cp->s);
  623.         AppendToUndobuff(ESC_STR);
  624.  
  625.         delline(1);
  626.         break;
  627.  
  628.           case 'p':    /* print the line */
  629.         if (P(P_NU)) {
  630.             outstr(mkline(nu));
  631.         }
  632.         outstr(T_CV);
  633.         outstr(format_line(cp->s, (int *) NULL));
  634.         outstr(T_CI);
  635.         outstr("\r\n");
  636.         break;
  637.         }
  638.         ndone++;
  639.     } else if (cmdchar == 'd') {
  640.         nuu++;
  641.     }
  642.     if (cp == up->linep)
  643.         break;
  644.     }
  645.  
  646.     if (ndone) {
  647.     switch (cmdchar) {
  648.  
  649.       case 'd':
  650.         S_NOT_VALID;
  651.         AppendPositionToUndobuff(0, 1);
  652.         if (ndone >= P(P_RP))
  653.         smsg("%d fewer line%c", ndone,
  654.              (ndone > 1) ? 's' : ' ');
  655.         break;
  656.  
  657.       case 'p':
  658.         wait_return();
  659.         break;
  660.     }
  661.     stuffReadbuff("^");
  662.     } else
  663.     msg("No match");
  664.  
  665.     free((char *) prog);
  666. }
  667.  
  668. /*
  669.  * Character Searches 
  670.  */
  671.  
  672. static char     lastc = NUL;    /* last character searched for */
  673. static int      lastcdir;    /* last direction of character search */
  674. static int      lastctype;    /* last type of search ("find" or "to") */
  675.  
  676. /*
  677.  * searchc(c, dir, type) 
  678.  *
  679.  * Search for character 'c', in direction 'dir'. If type is 0, move to the
  680.  * position of the character, otherwise move to just before the char. 
  681.  */
  682. bool_t
  683. searchc(c, dir, type)
  684.     char            c;
  685.     int             dir;
  686.     int             type;
  687. {
  688.     LPtr            save;
  689.  
  690.     save = *Curschar;        /* save position in case we fail */
  691.     lastc = c;
  692.     lastcdir = dir;
  693.     lastctype = type;
  694.  
  695.     /*
  696.      * On 'to' searches, skip one to start with so we can repeat searches in
  697.      * the same direction and have it work right. 
  698.      */
  699.     if (type)
  700.     (dir == FORWARD) ? oneright() : oneleft();
  701.  
  702.     while ((dir == FORWARD) ? oneright() : oneleft()) {
  703.     if (gchar(Curschar) == c) {
  704.         if (type)
  705.         (dir == FORWARD) ? oneleft() : oneright();
  706.         return TRUE;
  707.     }
  708.     }
  709.     *Curschar = save;
  710.     return FALSE;
  711. }
  712.  
  713. bool_t
  714. crepsearch(flag)
  715.     int             flag;
  716. {
  717.     int             dir = lastcdir;
  718.     int             rval;
  719.  
  720.     if (lastc == NUL)
  721.     return FALSE;
  722.  
  723.     rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
  724.  
  725.     lastcdir = dir;        /* restore dir., since it may have changed */
  726.  
  727.     return rval;
  728. }
  729.  
  730. /*
  731.  * "Other" Searches 
  732.  */
  733.  
  734. /*
  735.  * showmatch - move the cursor to the matching paren or brace 
  736.  */
  737. LPtr           *
  738. showmatch()
  739. {
  740.     static LPtr     pos;
  741.     int             (*move) (), inc(), dec();
  742.     char            initc = gchar(Curschar);    /* initial char */
  743.     char            findc;    /* terminating char */
  744.     char            c;
  745.     int             count = 0;
  746.  
  747.     pos = *Curschar;        /* set starting point */
  748.  
  749.     switch (initc) {
  750.  
  751.       case '(':
  752.     findc = ')';
  753.     move = inc;
  754.     break;
  755.       case ')':
  756.     findc = '(';
  757.     move = dec;
  758.     break;
  759.       case '{':
  760.     findc = '}';
  761.     move = inc;
  762.     break;
  763.       case '}':
  764.     findc = '{';
  765.     move = dec;
  766.     break;
  767.       case '[':
  768.     findc = ']';
  769.     move = inc;
  770.     break;
  771.       case ']':
  772.     findc = '[';
  773.     move = dec;
  774.     break;
  775.       default:
  776.     return (LPtr *) NULL;
  777.     }
  778.  
  779.     while ((*move) (&pos) != -1) {    /* until end of file */
  780.     c = gchar(&pos);
  781.     if (c == initc)
  782.         count++;
  783.     else if (c == findc) {
  784.         if (count == 0)
  785.         return &pos;
  786.         count--;
  787.     }
  788.     }
  789.     return (LPtr *) NULL;    /* never found it */
  790. }
  791.  
  792. /*
  793.  * findfunc(dir) - Find the next function in direction 'dir' 
  794.  *
  795.  * Return TRUE if a function was found. 
  796.  */
  797. bool_t
  798. findfunc(dir)
  799.     int             dir;
  800. {
  801.     LPtr           *curr;
  802.  
  803.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  804.  
  805.     curr = Curschar;
  806.  
  807.     do {
  808.     curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
  809.  
  810.     if (curr != NULL && curr->linep->s[0] == '{') {
  811.         setpcmark();
  812.         *Curschar = *curr;
  813.         return TRUE;
  814.     }
  815.     } while (curr != NULL);
  816.  
  817.     return FALSE;
  818. }
  819.  
  820. /*
  821.  * The following routines do the word searches performed by the 'w', 'W',
  822.  * 'b', 'B', 'e', and 'E' commands. 
  823.  */
  824.  
  825. /*
  826.  * To perform these searches, characters are placed into one of three
  827.  * classes, and transitions between classes determine word boundaries. 
  828.  *
  829.  * The classes are: 
  830.  *
  831.  * 0 - white space 1 - letters, digits, and underscore 2 - everything else 
  832.  */
  833.  
  834. static int      stype;        /* type of the word motion being performed */
  835.  
  836. #define C0(c)   (((c) == ' ') || ((c) == '\t') || ((c) == NUL))
  837. #define C1(c)   (isalpha(c) || isdigit(c) || ((c) == '_'))
  838.  
  839. /*
  840.  * cls(c) - returns the class of character 'c' 
  841.  *
  842.  * The 'type' of the current search modifies the classes of characters if a 'W',
  843.  * 'B', or 'E' motion is being done. In this case, chars. from class 2 are
  844.  * reported as class 1 since only white space boundaries are of interest. 
  845.  */
  846. static int
  847. cls(c)
  848.     char            c;
  849. {
  850.     if (C0(c))
  851.     return 0;
  852.  
  853.     if (C1(c))
  854.     return 1;
  855.  
  856.     /*
  857.      * If stype is non-zero, report these as class 1. 
  858.      */
  859.     return (stype == 0) ? 2 : 1;
  860. }
  861.  
  862.  
  863. /*
  864.  * fwd_word(pos, type) - move forward one word 
  865.  *
  866.  * Returns the resulting position, or NULL if EOF was reached. 
  867.  */
  868. LPtr           *
  869. fwd_word(p, type)
  870.     LPtr           *p;
  871.     int             type;
  872. {
  873.     static LPtr     pos;
  874.     int             sclass = cls(gchar(p));    /* starting class */
  875.  
  876.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  877.  
  878.     pos = *p;
  879.  
  880.     stype = type;
  881.  
  882.     /*
  883.      * We always move at least one character. 
  884.      */
  885.     if (inc(&pos) == -1)
  886.     return NULL;
  887.  
  888.     if (sclass != 0) {
  889.     while (cls(gchar(&pos)) == sclass) {
  890.         if (inc(&pos) == -1)
  891.         return NULL;
  892.     }
  893.     /*
  894.      * If we went from 1 -> 2 or 2 -> 1, return here. 
  895.      */
  896.     if (cls(gchar(&pos)) != 0)
  897.         return &pos;
  898.     }
  899.     /* We're in white space; go to next non-white */
  900.  
  901.     while (cls(gchar(&pos)) == 0) {
  902.     /*
  903.      * We'll stop if we land on a blank line 
  904.      */
  905.     if (pos.index == 0 && pos.linep->s[0] == NUL)
  906.         break;
  907.  
  908.     if (inc(&pos) == -1)
  909.         return NULL;
  910.     }
  911.  
  912.     return &pos;
  913. }
  914.  
  915. /*
  916.  * bck_word(pos, type) - move backward one word 
  917.  *
  918.  * Returns the resulting position, or NULL if top-of-file was reached. 
  919.  */
  920. LPtr           *
  921. bck_word(p, type)
  922.     LPtr           *p;
  923.     int             type;
  924. {
  925.     static LPtr     pos;
  926.     int             sclass = cls(gchar(p));    /* starting class */
  927.  
  928.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  929.  
  930.     pos = *p;
  931.  
  932.     stype = type;
  933.  
  934.     if (dec(&pos) == -1)
  935.     return NULL;
  936.  
  937.     /*
  938.      * If we're in the middle of a word, we just have to back up to the start
  939.      * of it. 
  940.      */
  941.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  942.     /*
  943.      * Move backward to start of the current word 
  944.      */
  945.     while (cls(gchar(&pos)) == sclass) {
  946.         if (dec(&pos) == -1)
  947.         return NULL;
  948.     }
  949.     inc(&pos);        /* overshot - forward one */
  950.     return &pos;
  951.     }
  952.     /*
  953.      * We were at the start of a word. Go back to the start of the prior
  954.      * word. 
  955.      */
  956.  
  957.     while (cls(gchar(&pos)) == 0) {    /* skip any white space */
  958.     /*
  959.      * We'll stop if we land on a blank line 
  960.      */
  961.     if (pos.index == 0 && pos.linep->s[0] == NUL)
  962.         return &pos;
  963.  
  964.     if (dec(&pos) == -1)
  965.         return NULL;
  966.     }
  967.  
  968.     sclass = cls(gchar(&pos));
  969.  
  970.     /*
  971.      * Move backward to start of this word. 
  972.      */
  973.     while (cls(gchar(&pos)) == sclass) {
  974.     if (dec(&pos) == -1)
  975.         return NULL;
  976.     }
  977.     inc(&pos);            /* overshot - forward one */
  978.  
  979.     return &pos;
  980. }
  981.  
  982. /*
  983.  * end_word(pos, type) - move to the end of the word 
  984.  *
  985.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  986.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  987.  * motion crosses blank lines. When the real vi crosses a blank line in an
  988.  * 'e' motion, the cursor is placed on the FIRST character of the next
  989.  * non-blank line. The 'E' command, however, works correctly. Since this
  990.  * appears to be a bug, I have not duplicated it here. 
  991.  *
  992.  * Returns the resulting position, or NULL if EOF was reached. 
  993.  */
  994. LPtr           *
  995. end_word(p, type)
  996.     LPtr           *p;
  997.     int             type;
  998. {
  999.     static LPtr     pos;
  1000.     int             sclass = cls(gchar(p));    /* starting class */
  1001.  
  1002.     S_CHECK_TOPCHAR_AND_BOTCHAR;
  1003.  
  1004.     pos = *p;
  1005.  
  1006.     stype = type;
  1007.  
  1008.     if (inc(&pos) == -1)
  1009.     return NULL;
  1010.  
  1011.     /*
  1012.      * If we're in the middle of a word, we just have to move to the end of
  1013.      * it. 
  1014.      */
  1015.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  1016.     /*
  1017.      * Move forward to end of the current word 
  1018.      */
  1019.     while (cls(gchar(&pos)) == sclass) {
  1020.         if (inc(&pos) == -1)
  1021.         return NULL;
  1022.     }
  1023.     dec(&pos);        /* overshot - forward one */
  1024.     return &pos;
  1025.     }
  1026.     /*
  1027.      * We were at the end of a word. Go to the end of the next word. 
  1028.      */
  1029.  
  1030.     while (cls(gchar(&pos)) == 0) {    /* skip any white space */
  1031.     if (inc(&pos) == -1)
  1032.         return NULL;
  1033.     }
  1034.  
  1035.     sclass = cls(gchar(&pos));
  1036.  
  1037.     /*
  1038.      * Move forward to end of this word. 
  1039.      */
  1040.     while (cls(gchar(&pos)) == sclass) {
  1041.     if (inc(&pos) == -1)
  1042.         return NULL;
  1043.     }
  1044.     dec(&pos);            /* overshot - forward one */
  1045.  
  1046.     return &pos;
  1047. }
  1048.